package org.fcitmuk.openclinica.db;

import java.util.Vector;

import org.fcitmuk.db.util.Record;
import org.fcitmuk.epihandy.EpihandyConstants;
import org.fcitmuk.epihandy.FormDef;
import org.fcitmuk.epihandy.StudyDef;
import org.fcitmuk.midp.db.util.Storage;
import org.fcitmuk.midp.db.util.StorageFactory;
import org.fcitmuk.openclinica.CohortList;
import org.fcitmuk.openclinica.Subject;
import org.fcitmuk.openclinica.SubjectData;
import org.fcitmuk.openclinica.SubjectFieldList;
import org.fcitmuk.openclinica.SubjectFieldValueList;
import org.fcitmuk.openclinica.SubjectForm;
import org.fcitmuk.openclinica.SubjectList;


public class OpenclinicaDataStorage {
	/** The unique identifier for storage of subjects. */
	private static final String SUBJECT_STORAGE_NAME = "org.fcitmuk.openclinica.Subject";
	
	/** The unique identifier for storage of subject fields. */
	private static final String SUBJECT_FIELD_STORAGE_NAME = "openclinica.SubjectFieldList";
	
	/** The unique identifier for storage of cohorts. */
	private static final String COHORT_STORAGE_NAME = "openclinica.CohortList";
		
	/** The unique identifier for storage of subject field values. */
	private static final String SUBJECT_FIELD_VALUE_STORAGE_NAME = "openclinica.SubjectFieldValueList";

	/** The unique identifier for storage of subject form record mappings. */
	private static final String SUBJECT_FORM_STORAGE_NAME = "org.openclinica.SubjectForm";

	
	/**
	 * Saves a subject form mapping.
	 * 
	 * @param formDefId - the form definition identifier.
	 * @param subjectForm - the subject form mapping data
	 */
	public static void saveSubjectForm(int formDefId, SubjectForm subjectForm){
		Storage store = StorageFactory.getStorage(getSubjectFormStorageName(formDefId),null);
		store.save(subjectForm); //These records are not edited, they are only saved new and deleted.
	}
	
	/**
	 * Saves a subject.
	 * 
	 * @param subject - the subject to be saved.
	 */
	public static void saveSubject(Subject subject){
		Storage store = StorageFactory.getStorage(getSubjectStorageName(),null);
		store.save((Record)subject);
	}
	
	/**
	 * Saves subjects.
	 * 
	 * @param subjects - the list of subjects to be saved.
	 */
	public static void saveSubjects(SubjectList subjects){
		Storage store = StorageFactory.getStorage(getSubjectStorageName(),null);
		store.delete();
		for(int i=0; i<subjects.size(); i++)
			store.save(subjects.getSubject(i));
	}
	
	/**
	 * Saves subject data. All existing data is first deleted before
	 * the new one is saved.
	 * 
	 * @param subjectData - the subject data to be saved.
	 */
	public static void saveSubjectData(SubjectData subjectData){
		if(subjectData == null)
			return;
		
		Storage store = null;
		
		SubjectList subjects = subjectData.getSubjects();
		if(subjects != null && subjects.size() > 0){
			store = StorageFactory.getStorage(getSubjectStorageName(),null);
			store.delete();
			for(int i=0; i<subjects.size(); i++)
				store.save(subjects.getSubject(i));
		}
		
		SubjectFieldList fields = subjectData.getFields();
		if(fields != null && fields.size() > 0){
			store = StorageFactory.getStorage(getSubjectFieldStorageName(),null);
			store.delete();
			store.addNew(fields);
		}
		
		SubjectFieldValueList values = subjectData.getFieldValues();
		if(values != null && values.size() > 0){
			store = StorageFactory.getStorage(getSubjectFieldValueStorageName(),null);
			store.delete();
			store.addNew(values);
		}
	}
	
	/**
	 * Saves cohort data. All existing data is first deleted before
	 * the new one is saved.
	 * 
	 * @param cohorts - the cohort list.
	 */
	public static void saveCohorts(CohortList cohorts){
		if(cohorts == null)
			return;
		
		Storage store = null;
		if(cohorts != null && cohorts.size() > 0){
			store = StorageFactory.getStorage(getCohortStorageName(),null);
			store.delete();
			store.addNew(cohorts);
		}
	}
	
	/**
	 * Gets a list of subjects.
	 * 
	 * @return - a list of subjects.
	 */
	public static Vector getSubjects(){
		Storage store = StorageFactory.getStorage(getSubjectStorageName(),null);
		return store.read(new Subject().getClass());
		

	}
	
	/**
	 * Gets a list of subjects matching a name and or a subject identifier parameters.
	 * Subjects whose names and identifier contain the search parameters
	 * will be returned. In otherwards, not an exact match.
	 * If a parameter is empty or null, all subjects are considered to match that parameter.
	 * For instance, if you pass null or empty name and identifier, all subjects
	 * will be returned.
	 * 
	 * @param name - the name to search for
	 * @param identifier - the identifier
	 * @return
	 */
	public static Vector getSubjects(String identifier,String name){
		Vector subjects  = getSubjects();

		Vector matchedSubjects = new Vector();
		
		if(subjects != null){
			Subject subject;
			for(int i=0; i<subjects.size(); i++){
				subject = (Subject)subjects.elementAt(i);
				if(doesSubjectMatch(identifier,name,subject))
					matchedSubjects.addElement(subject);
			}
		}
		
		return matchedSubjects;
	}
	
	/**
	 * Checks whether a subject matches the search parameters.
	 * 
	 * @param name - the name parameters.
	 * @param identifier - the identifier parameter.
	 * @param subject - the subject.
	 * @return - true if the subject matches, else false.
	 */
	private static boolean doesSubjectMatch( String searchIdentifier,String searchName, Subject subject){
		boolean nameMatch = false;
		boolean identifierMatch = false;
		
		if(searchName == null || searchName.equals(""))
			nameMatch = true;
		
		if(searchIdentifier == null || searchIdentifier.equals(""))
			identifierMatch = true;
		
		if(!identifierMatch)
			identifierMatch = doesPatternMatch(searchIdentifier,subject.getSubjectIdentifier());
		
		if(!nameMatch)
			nameMatch = doesPatternMatch(searchName,subject.getFamilyName());
		
		if(!nameMatch)
			nameMatch = doesPatternMatch(searchName,subject.getMiddleName());
		
		if(!nameMatch)
			nameMatch = doesPatternMatch(searchName,subject.getGivenName());
		
		return nameMatch && identifierMatch;
	}
	
	private static boolean doesPatternMatch(String searchPattern, String value){
		if(value != null && value.indexOf(searchPattern) != -1)
				return true;
		return false;
	}
	
	/**
	 * Gets subject database fields.
	 * 
	 * @return - subject database field list object.
	 */
	public static SubjectFieldList getSubjectFields(){
		Storage store = StorageFactory.getStorage(getSubjectFieldStorageName(),null);
		SubjectFieldList fieldList = null;
		Vector vect = store.read(new SubjectFieldList().getClass()); 
		if(vect != null && vect.size() > 0)
			fieldList = (SubjectFieldList)vect.elementAt(0); //We can only have one per storage.
		return fieldList;
	}
	
	/**
	 * Gets cohorts.
	 * 
	 * @return - cohort list object.
	 */
	public static CohortList getCohorts(){
		Storage store = StorageFactory.getStorage(getCohortStorageName(),null);
		CohortList cohortList = null;
		Vector vect = store.read(new CohortList().getClass()); 
		if(vect != null && vect.size() > 0)
			cohortList = (CohortList)vect.elementAt(0); //We can only have one per storage.
		return cohortList;
	}
	
	/**
	 * Gets subject database field values.
	 * 
	 * @return - subject database field value list object.
	 */
	public static SubjectFieldValueList getSubjectFieldValues(){
		Storage store = StorageFactory.getStorage(getSubjectFieldValueStorageName(),null);
		SubjectFieldValueList fieldValueList = null;
		Vector vect = store.read(new SubjectFieldValueList().getClass());
		if(vect != null && vect.size() > 0)
			fieldValueList = (SubjectFieldValueList)vect.elementAt(0); //We can only have one per storage.
		return fieldValueList;
	}
	
	/**
	 * Deletes a subject.
	 * When you delete a subject, all forms collected about them are deleted
	 * including the form that created this subject.
	 * Is this really necessary functionality?
	 * 
	 * @param data - the subject to be deleted.
	 */
	public static void deleteSubject(Subject subject){
		Storage store = StorageFactory.getStorage(getSubjectStorageName(),null);
		store.delete(subject);
	}
	
	/**
	 * Delete all subjects from storage.
	 *
	 */
	public static void deleteSubjects(){
		Storage store = StorageFactory.getStorage(getSubjectStorageName(),null);
		store.delete();
	}
	
	public static SubjectForm getSubjectForm(Integer subjectId, int formDefId){
		Storage store = StorageFactory.getStorage(getSubjectFormStorageName(formDefId),null);
		Vector vect = store.read(new SubjectForm().getClass());
		if(vect != null && vect.size() > 0){
			SubjectForm subjectForm = null;
			for(int i=0; i<vect.size(); i++){
				subjectForm = (SubjectForm)vect.elementAt(i); 
				if(subjectId.equals(subjectForm.getSubjectId()))
					return subjectForm;
			}
		}
		return null;
	}
	
	public static void deleteSubjectForm(Integer subjectId, int formDefId){
		SubjectForm subjectForm = getSubjectForm(subjectId,formDefId);
		if(subjectForm != null)
			StorageFactory.getStorage(getSubjectFormStorageName(formDefId),null).delete(subjectForm);
	}
	
	public static void deleteSubjectForms(Vector studies){
		for(int i=0; i<studies.size(); i++){
			StudyDef studyDef = (StudyDef)studies.elementAt(i);
			deleteSubjectForms(studyDef);
		}
	}
	
	public static void deleteSubjectForms(StudyDef studyDef){
		for(byte i=0; i<studyDef.getForms().size(); i++){
			FormDef formDef = studyDef.getFormAt(i);
			Storage store = StorageFactory.getStorage(getSubjectFormStorageName(formDef.getId()),null);
			store.delete();
		}
	}
	
	/**
	 * Gets the recordId of a form entered for a subject.
	 * @param subjectId
	 * @param formDefId
	 * @return
	 */
	public static int getSubjectFormRecordId(Integer subjectId, int formDefId){
		SubjectForm subjectForm = getSubjectForm(subjectId,formDefId);
		if(subjectForm != null)
			return subjectForm.getFormRecordId();
		return EpihandyConstants.NULL_ID;
	}
	
	public static Vector getSubjectForms(int formDefId){
		Storage store = StorageFactory.getStorage(getSubjectFormStorageName(formDefId),null);
		return store.read(new SubjectForm().getClass());
	}

	/**
	 * Gets the name of the storage for subjects.
	 * 
	 * @return - the storage name
	 */
	private static  String getSubjectStorageName(){
		return SUBJECT_STORAGE_NAME;
	}
	
	/**
	 * Gets the name of the storage for subject fields.
	 * 
	 * @return - the storage name
	 */
	private static  String getSubjectFieldStorageName(){
		return SUBJECT_FIELD_STORAGE_NAME;
	}
	
	/**
	 * Gets the name of the storage for cohorts.
	 * 
	 * @return - the storage name
	 */
	private static  String getCohortStorageName(){
		return COHORT_STORAGE_NAME;
	}
	
	/**
	 * Gets the name of the storage for subject field values.
	 * 
	 * @return - the storage name
	 */
	private static  String getSubjectFieldValueStorageName(){
		return SUBJECT_FIELD_VALUE_STORAGE_NAME;
	}
	
	/**
	 * Gets the name of the storage for subject form record mappings.
	 * For performance, subject form record mappings are stored separately
	 * for each form type. This will not only reduce the size per record
	 * but will also incread the search speed as less records will have
	 * to be searched through when locating a subject form record mapping.
	 * As a result of this, each subject will have one record in this storage.
	 * 
	 * @param - formDefId - the form definition identifier.
	 * @return - the storage name
	 */
	private static  String getSubjectFormStorageName(int formDefId){
		return SUBJECT_FORM_STORAGE_NAME + "." + formDefId;
	}
}
